#!/usr/bin/python3

# -*- coding: UTF-8 -*-


'''

@author: tony
'''


import sys
import os

opcode_dict = {
    'HLT'   :   ('00' + '00' + '00'     ,   '2-2-2-5Z-5Z'   ,   True),

    'LUI'   :   ('00' + '00' + '01'     ,   '2-2-2-10I'     ,   True),

    'CSRRS' :   ('00' + '01' + '00'     ,   '2-2-2-5I-5Z'   ,   True),
    'CSRRC' :   ('00' + '01' + '01'     ,   '2-2-3-5I-5Z'   ,   True),

    'CSRRW' :   ('00' + '01' + '10'     ,   '2-2-3-5R-5Z'   ,   True),
    'CSRRR' :   ('00' + '01' + '11'     ,   '2-2-3-5R-5Z'   ,   True),

    'JALR'  :   ('00' + '10'            ,   '2-2-12I'       ,   True),
    'JAL'   :   ('00' + '11'            ,   '2-2-12I'       ,   True),
    'BEQ'   :   ('01' + '00'            ,   '2-2-12I'       ,   True),
    'BNE'   :   ('01' + '01'            ,   '2-2-12I'       ,   True),
    'BLT'   :   ('01' + '10'            ,   '2-2-12I'       ,   True),
    'BGE'   :   ('01' + '11'            ,   '2-2-12I'       ,   True),

    'ADD'   :   ('10' + '0000'          ,   '2-4-5R-5R'     ,   True),
    'SUB'   :   ('10' + '0001'          ,   '2-4-5R-5R'     ,   True),
    'SLT'   :   ('10' + '0010'          ,   '2-4-5R-5R'     ,   True),
    'SLTU'  :   ('10' + '0011'          ,   '2-4-5R-5R'     ,   True),
    'SLL'   :   ('10' + '0100'          ,   '2-4-5R-5R'     ,   True),
    'SRL'   :   ('10' + '0101'          ,   '2-4-5R-5R'     ,   True),
    'SRA'   :   ('10' + '0110'          ,   '2-4-5R-5R'     ,   True),
    'XOR'   :   ('10' + '0111'          ,   '2-4-5R-5R'     ,   True),
    'OR'    :   ('10' + '1000'          ,   '2-4-5R-5R'     ,   True),
    'AND'   :   ('10' + '1001'          ,   '2-4-5R-5R'     ,   True),
    'MUL'   :   ('10' + '1010'          ,   '2-4-5R-5R'     ,   True),
    'MULU'  :   ('10' + '1011'          ,   '2-4-5R-5R'     ,   True),

    'ADDI'  :   ('10' + '1100'          ,   '2-4-5R-5I'     ,   True),
    'SLTI'  :   ('10' + '1101'          ,   '2-4-5R-5I'     ,   True),
    'SLTIU' :   ('10' + '1110'          ,   '2-4-5R-5I'     ,   True),
    'SLLI'  :   ('10' + '1111'          ,   '2-4-5R-5I'     ,   True),
    'SRLI'  :   ('11' + '0000'          ,   '2-4-5R-5I'     ,   True),
    'SRAI'  :   ('11' + '0001'          ,   '2-4-5R-5I'     ,   True),
    'XORI'  :   ('11' + '0010'          ,   '2-4-5R-5I'     ,   True),
    'ORI'   :   ('11' + '0011'          ,   '2-4-5R-5I'     ,   True),
    'ANDI'  :   ('11' + '0100'          ,   '2-4-5R-5I'     ,   True),

    'LH'    :   ('11' + '1000'          ,   '2-4-2R-8I'     ,   True),
    'SH'    :   ('11' + '1001'          ,   '2-4-2R-8I'     ,   True),
    'LW'    :   ('11' + '1010'          ,   '2-4-2R-8I'     ,   True),
    'SW'    :   ('11' + '1011'          ,   '2-4-2R-8I'     ,   True),

    'DIV'   :   ('11' + '1100'          ,   '2-4-5R-5R'     ,   False),
    'SQRT'  :   ('11' + '1101'          ,   '2-4-5R-5R'     ,   False),
    'COS'   :   ('11' + '1110'          ,   '2-4-5R-5R'     ,   False)
    }

regs_dict = {
    'AL' : ('00000' , 16),
    'BL' : ('00001' , 16),
    'CL' : ('00010' , 16),
    'DL' : ('00011' , 16),
    'EL' : ('00100' , 16),
    'FL' : ('00101' , 16),
    'GL' : ('00110' , 16),
    'HL' : ('00111' , 16),

    'AH' : ('01000' , 16),
    'BH' : ('01001' , 16),
    'CH' : ('01010' , 16),
    'DH' : ('01011' , 16),
    'EH' : ('01100' , 16),
    'FH' : ('01101' , 16),
    'GH' : ('01110' , 16),
    'HH' : ('01111' , 16),

    'AX' : ('11000' , 32),
    'BX' : ('11001' , 32),
    'CX' : ('11010' , 32),
    'DX' : ('11011' , 32),
    'EX' : ('11100' , 32),
    'FX' : ('11101' , 32),
    'GX' : ('11110' , 32),
    'HX' : ('11111' , 32)
}

RAM_DEPTH  = 256
CODE_DEPTH = 4096
ram = {}
ram_addr_ptr = RAM_DEPTH - 1
code = []
ram_context  = ['0000000000000000' for i in range(RAM_DEPTH)]
code_context = []


if __name__ == '__main__':

    # 打开待编译的文件
    try:
        ast16_file = open(sys.argv[1], 'r')
    except:
        print("Open .ast16 file failed.")

    # 预处理

    # 语法检查，检查是否有非法字符和格式


    # 删除注释，删除空行


    # 逐行翻译
    for line in ast16_file:
        line = line.strip() #去除前后空格

        if not len(line) or line.startswith('#'): #去除空行和注释
            pass

        elif line.startswith('INT'): #提取变量定义行
            tmp = line.split('=')
            var_type  = tmp[0].strip().split(' ')[0]
            var_name  = tmp[0].strip().split(' ')[1]
            var_value_ori = tmp[1].strip()
            var_value_bin = bin(int(var_value_ori)).split('b')[1]
            if var_type == 'INT16' :
                var_addr  = ram_addr_ptr
                if len(var_value_bin) > 16 : print('ERROR: ' + line )
                for i in range(16 - len(var_value_bin)) : var_value_bin = '0' + var_value_bin
                ram_context[var_addr] = var_value_bin
                ram_addr_ptr = ram_addr_ptr - 1
            elif var_type == 'INT32' :
                var_addr  = ram_addr_ptr - 1
                if len(var_value_bin) > 32 : print('ERROR: ' + line )
                for i in range(32 - len(var_value_bin)) : var_value_bin = '0' + var_value_bin
                ram_context[var_addr] = var_value_bin[:-16]    # 低地址存低位
                ram_context[var_addr + 1] = var_value_bin[16:] # 高地址存高位
                ram_addr_ptr = ram_addr_ptr - 2
            ram[var_name] = (var_type , var_value_ori, var_addr)

        elif opcode_dict[line.split(' ')[0]]: #提取代码行
            op = line.split(' ')[0]
            op_prefix = opcode_dict[op][0]
            op_fortmat = opcode_dict[op][1]
            #根据指令类型解析操作码
            if op_fortmat == '2-2-2-5Z-5Z':
                op_src_a = '00000'
                op_src_b = '00000'
                op_dst   = ''
                pass

            elif op_fortmat == '2-2-2-10I':
                op_src_a = line.split(' ')[-1]
                op_src_a = bin(int(op_src_a)).split('b')[1]
                if len(op_src_a) > 10 : print('ERROR: ' + line)
                for i in range(10 - len(op_src_a)) : op_src_a = '0' + op_src_a
                op_src_b = ''
                op_dst   = ''
                pass

            elif op_fortmat == '2-2-2-5I-5Z':
                op_src_a = line.split(' ')[-1]
                op_src_a = bin(int(op_src_a)).split('b')[-1]                    #立即数由十进制转化为二进制
                if len(op_src_a) > 5 : print('ERROR: ' + line)                  #立即数长度合法性检测
                for i in range(5 - len(op_src_a)) : op_src_a = '0' + op_src_a   #立即数长度补齐
                op_src_b = '00000'
                op_dst   = ''
                pass

            elif op_fortmat == '2-2-2-5R-5Z':
                op_src_a = regs_dict[line.split(' ')[-1]][0]
                op_src_b = '00000'
                op_dst   = ''
                pass

            elif op_fortmat == '2-2-12I':
                op_src_a = line.split(' ')[1].strip()
                op_src_a = bin(int(op_src_a)).split('b')[1]
                if len(op_src_a) > 12 : print('ERROR: ' + line )
                for i in range(12 - len(op_src_a)) : op_src_a = '0' + op_src_a
                op_src_b = ''
                op_dst   = ''
                pass

            elif op_fortmat == '2-4-5R-5R':
                op_src_a = regs_dict[line.split(',')[0].split(' ')[1].strip()][0]
                op_src_b = regs_dict[line.split(',')[1].strip()][0]
                op_dst   = ''
                pass

            elif op_fortmat == '2-4-5R-5I':
                op_src_a = regs_dict[line.split(',')[0].split(' ')[1].strip()][0]
                op_src_b = line.split(',')[1].strip()
                op_src_b = bin(int(op_src_b)).split('b')[1]
                if len(op_src_b) > 5 : print('ERROR: ' + line )
                for i in range(5 - len(op_src_b)) : op_src_b = '0' + op_src_b
                op_dst   = ''
                pass

            elif op_fortmat == '2-4-2R-8I':
                # op_src_a的生成比较特殊，因为寄存器idx只有2bit，非标准的5bit寄存器idx，因此这里只取了寄存器的最后两位
                # 因此在实现汇编编程时，操作码1只能是AL，BL，CL，DL（对应LH和SH）及AX，BX，CX，DX（对应LW，SW）
                # 这里还要特别说明一下，CPU在设计时，目的寄存器默认为AL/AX，也是考虑写内存时候减少寄存器数据的搬移，提高效率
                op_src_a = regs_dict[line.split(',')[0].split(' ')[1].strip()][0][:2]
                op_src_b = line.split(',')[1].strip()
                if not op_src_b.isdigit() : op_src_b = ram[op_src_b][2]
                op_src_b = bin(int(op_src_b)).split('b')[1]
                if len(op_src_b) > 8 : print('ERROR: ' + line )
                for i in range(8 - len(op_src_b)) : op_src_b = '0' + op_src_b
                op_dst   = ''
                pass

            code.append((op, op_fortmat, op_prefix, op_src_a, op_src_b, op_dst))
            code_context.append(op_prefix + op_src_a + op_src_b + op_dst)

        else:
            print('ERROR : ' + line)


    # 填充未包含代码的代码空间的初始数据为0
    for i in range(4096 - len(code_context)) :
        code_context.append('0000000000000000')


    # 生成二进制的ram和code的内存初始化文件
    try:
        ram_init_file_bin = open(str(ast16_file.name).split('.')[-2].split('/')[-1] + '_ram.bin', 'w')
        code_init_file_bin = open(str(ast16_file.name).split('.')[-2].split('/')[-1] + '_code.bin', 'w')
    except:
        print("Create file failed.")
     
    for line in ram_context :
        ram_init_file_bin.write(line + '\n')
    for line in code_context :
        code_init_file_bin.write(line + '\n')

    # 关闭文件
    ast16_file.close()
    ram_init_file_bin.close()
    code_init_file_bin.close()


    # for key, value in ram.items() :
    #     print(key + '\t' + value[0] + '\t' + value[1] + '\t' + str(value[2]))
    i = 0
    for item in code:
        instr_bin = item[2] + item[3] + item[4] + item[5] 
        instr_dec = int('0b' + instr_bin , 2)
        instr_hex = hex(instr_dec)
        print(str(i) + '\t' + item[0] + '\t' + item[2] + item[3] + item[4] + item[5] + '\t' + instr_hex + '\t' + item[1])
        i = i + 1

    # 生成16进制的内存初始化文件
    try:
        ram_init_file_hex = open(str(ast16_file.name).split('.')[-2].split('/')[-1] + '_ram.hex', 'w')
        code_init_file_hex = open(str(ast16_file.name).split('.')[-2].split('/')[-1] + '_code.hex', 'w')
    except:
        print("Create file failed.")

    for line in ram_context :
        iline = int('0b' + line , 2)
        hline = hex(iline)
        ram_init_file_hex.write(hline.split('x')[1] + '\n')

    for line in code_context :
        iline = int('0b' + line , 2)
        hline = hex(iline)
        code_init_file_hex.write(hline.split('x')[1] + '\n')
    
    ram_init_file_hex.close()
    code_init_file_hex.close()

    os.system("mv *.bin *.hex ../tests")

    pass
